/**
 * weapp-js v2.2.3(2022-4-26)
 * https://codeup.aliyun.com/motigo/motigo/weapp_base
 * Copyright@ 2022 Motigo, Inc.
 * 
 */
import Preload from './preload.js';
import ConsoleLog from './log.js';
import Callback from './utils/callback';

// InnerAudioContext扩展，规避一些无法播放，无回调等问题
class InnerAudioContextEx extends Callback {
    constructor() {
        super();

        this._audio = null;
        this._lastUrl = null;
        this.urls = '';
        this.opts = {};
        this.playOpts = {};
    }

    play(opts) {
        console.log('play');
        console.log(opts);
        let urls = opts.urls || [];
        // 停止上一个
        this.stop();
        if (!opts.urls) {
            this.fail(opts, { err: 'url链接不能为空' });
            return;
        }
        if (typeof opts.urls != 'object') {
            if (opts.urls.indexOf('http')) {
                this.fail(opts, { err: '请传入合法的链接' });
                return;
            }
        }
        this.urls = urls;

        this.playOpts = {};

        opts = opts || {};
        for (let key in opts) {
            //this.opts[key] = opts[key];
            this.playOpts[key] = opts[key];
        }

        this.opts = opts;

        if (urls instanceof Array) {
            console.log(
                `[mox.audio]==============req play ${urls.length} audio, urls: `,
                urls
            );
            this._multiPlayCallback(urls, 0, opts);
        } else if (typeof urls == 'string') {
            console.log(`[mox.audio]==============req play url: ${urls}`);
            this._play(urls, opts);
        } else {
            console.error('[mox.audio]invalid play urls param:', urls);
            // opts.fail &&
            //     opts.fail({
            //         url: urls,
            //         err: 'invalid urls param'
            //     });
            this.fail(opts, {
                urls: urls,
                err: 'invalid urls param',
            });

            // opts.complete &&
            //     opts.complete({
            //         url: urls,
            //         err: 'invalid urls param'
            //     });
            this.complete(opts, {
                urls: urls,
                err: 'invalid urls param',
            });
        }
    }

    _multiPlayCallback(urls, index, opts) {
        opts.result = opts.result || {
            successed: [],
            failed: [],
        }; // 通过opts.result保存过程结果
        if (index < urls.length) {
            this._play(urls[index], {
                success: (res) => {
                    opts.result.successed.push(res.url);
                    console.log(`[mox.audio]play successed: ${res.url}`);
                },
                fail: (res) => {
                    console.warn(`[mox.audio]play fail: ${res.url}`);
                    opts.result.failed.push(res.url);
                },
                complete: () => {
                    this._multiPlayCallback(urls, index + 1, opts);
                },
                begin: (res) => {
                    // res;
                    this.callback(opts, 'begin', {
                        index: index,
                        url: res.url,
                    });
                },
                progress: (res) => {
                    //console.log('play progress:' ,res);
                    // res;
                    this.callback(opts, 'progress', {
                        index: index,
                        url: res.url,
                    });
                },
            });
        } else {
            // play end
            if (opts.result.successed.length == urls.length) {
                // opts.success && opts.success(opts.result);
                this.success(opts, opts.result);
            } else {
                opts.result.err = `${opts.result.failed.length} failed`;
                // opts.fail && opts.fail(opts.result);
                this.fail(opts, opts.result);
            }

            // opts.complete && opts.complete(opts.result);
            this.complete(opts, opts.result);

            this.opts = {};
        }
    }

    stop() {
        if (this._audio) {
            this._audio.offError();
            this._audio.offPlay();
            this._audio.offEnded();
            this._audio.offWaiting();
            this._audio.stop();
            this._audio.destroy();
            this._audio = null;

            this._emitExclusiveEvent(this.opts, 'complete', {
                url: this.urls,
            });
        }
    }

    pause() {
        if (this._audio) {
            // this._audio.required_pause = true;  // 标记为主动暂停
            this._audio.pause();
            return true;
        } else {
            return false;
        }
    }

    resume() {
        console.log('[mox.audio]resume');
        if (this._audio && this._audio.paused && !this._audio.ended) {
            if (!this._audio.playing) {
                console.log('[mox.audio]replay audio');
                this._audio.stop();
                if (
                    this._lastUrl &&
                    typeof this._lastUrl == 'string' &&
                    this._lastUrl.length > 0
                ) {
                    this._audio.src = this._lastUrl;
                }
            } else {
                // this._audio.play(); // 不要和下面重复调用
            }

            this._audio.play();
            return true;
        } else {
            console.log('[mox.audio]ignored');
            return false;
        }
    }

    replay() {
        wx.nextTick(() => {
            this.play(this.urls, this.playOpts);
        });
    }

    // 播放单个
    _play(url, opts) {
        // const resUrl = Preload.getPath({url});
        Preload.getPath({
            url,
            success: (resUrl) => {
                // const resUrl = url;
                console.info(`[mox.audio]try play: ${url}, res url: ${resUrl}`);
                if (!this._audio) {
                    this._audio = wx.createInnerAudioContext();
                }

                this._audio.offError();
                this._audio.offPlay();
                this._audio.offEnded();
                this._audio.offWaiting();
                this._audio.offTimeUpdate();

                this._clearTimer();
                this._audio.ended = false; // ended在onEnded设置，避免resume的时候重新播放

                // 记录是否开始播放，用于规避在iOS上，resume无效的问题
                // 在ios是，如果audio设置了src，但还没有onPlay，这是关闭小程序再打开，调用resume音频不会继续播放！
                // 需要设置url
                this._audio.playing = false;

                this._audio.onError((e) => {
                    console.error('[mox.audio]on play error:', e);

                    // 经验证，如果设置到src的url不符合规则(如非http(s)://开头，又不是本地文件时），会使用之前设置的url播放
                    // 为了避免传错误的url却播放之前的音频，这里destroy
                    this._audio.destroy();
                    this._audio = null;
                    this._clearTimer();

                    const res = {
                        url: url,
                        detail: e,
                        err: e.errMsg,
                    };

                    this._emitExclusiveEvent(opts, 'fail', res);
                    this._emitExclusiveEvent(opts, 'complete', res);

                    ConsoleLog.error('play audio error');
                    ConsoleLog.error(e);
                });

                this._audio.onWaiting(() => {
                    console.info('[mox.audio]on waiting:', url);
                    opts.progress &&
                        opts.progress({
                            url: url,
                        });
                });

                this._audio.onEnded(() => {
                    if (this._audio) {
                        // 防止this._audio被异步设置为null
                        this._audio.ended = true;
                    }
                    this._clearTimer();

                    this._emitExclusiveEvent(opts, 'success', {
                        url: url,
                    });

                    this._emitExclusiveEvent(opts, 'complete', {
                        url: url,
                    });
                });

                this._audio.onPlay(() => {
                    // console.log('on play: ', this._audio.currentTime, this._audio.duration);
                    this._audio.playing = true;
                    opts.begin &&
                        opts.begin({
                            url: url,
                        });
                });

                // 重要！坑：必须设置onCanplay，才会有onTimeUpdate回调
                // this._audio.onCanplay( ()=>{
                //     console.log('on can play: ', this._audio.currentTime, this._audio.duration);
                // });

                // this._audio.onTimeUpdate( () => {
                //     // console.log('on time update: ', this._audio.currentTime, this._audio.duration);
                //     // if (this._audio.duration > 0 && !this._audio.timeout) {
                //     //     this._audio.timeout = setTimeout(()=>{
                //     //         console.log('on audio timeout', this._audio.currentTime, this._audio.duration, this._audio.paused);

                //     //         const res = {
                //     //             url: url,
                //     //             err: 'timeout'
                //     //         };

                //     //         this._emitExclusiveEvent(opts, 'fail', res);
                //     //         this._emitExclusiveEvent(opts, 'complete', res);
                //     //     }, this._audio.duration*1000+100);
                //     // }
                // });

                this._audio.src = encodeURI(resUrl);

                if (opts.loop == true) {
                    this._audio.loop = true;
                }

                if (this._lastUrl == this._audio.src) {
                    this._audio.seek(0);
                    this._audio.play();
                } else {
                    this._audio.autoplay = true;
                    this._lastUrl = url; //this._audio.src;
                }
            },
        });
    }

    // 触发opts中的独占的事件，主要是避免多次触发
    _emitExclusiveEvent(opts, evtName, params) {
        opts = opts || {};
        const cb = opts[evtName]; // 保存下来，因为需要马上将opts[evtName]置空, 避免因callback耗时导致重复回调

        opts[evtName] = null;
        cb && cb(params);
    }

    _clearTimer() {
        if (this._audio && this._audio.timeout) {
            clearTimeout(this._audio.timeout);
            this._audio.timeout = null;
        }
    }
}

class Audio extends Callback {
    constructor() {
        super();
        this._audios = [];
        this._globalAudio = null; // 独占的全局audio
        this._inited = false;
    }
    _init() {
        if (this._inited) {
            return false;
        }

        console.log('init audio mgr');
        this._inited = true;
        wx.setInnerAudioOption({
            mixWithOther: true,
            obeyMuteSwitch: false,
            success: () => {
                console.log('set inner audio option succes');
            },
            fail: (err) => {
                err;
                // console.warn('set inner audio option fail! err:', err);
            },
        });

        wx.onAudioInterruptionEnd(() => {
            console.log('[mox.audio]onAudioInterruptionEnd');
            this.resumeAll();
        });

        wx.onAudioInterruptionBegin(() => {
            console.log('[mox.audio]onAudioInterruptionBegin');
        });

        // wx.onAppShow(()=>{
        //     console.log('onAppShow');
        // });

        // wx.onAppHide(()=>{
        //     console.log('onAppHide');
        // });
    }
    play(opts) {
        if (!this._globalAudio) {
            this._globalAudio = this.createAudioContext();
        }

        this._globalAudio.play(opts);
    }
    stop() {
        if (this._globalAudio) {
            this._globalAudio.stop();
        }
    }
    pause() {
        if (this._globalAudio) {
            this._globalAudio.pause();
            return true;
        } else {
            return false;
        }
    }
    resume() {
        if (this._globalAudio) {
            this._globalAudio.resume();
        }
    }
    getGlobalAudio() {
        if (!this._globalAudio) {
            this._globalAudio = this.createAudioContext();
        }

        return this._globalAudio;
    }
    createAudioContext() {
        // 创建
        this._init();

        let audio = new InnerAudioContextEx();
        this._audios.push(audio);
        return audio;
    }
    destroyAudioContext(obj) {
        if (obj) {
            obj.stop();
            const index = this._audios.indexOf(obj);
            if (index > -1) {
                this._audios.splice(index, 1);
            }
        }
    }
    pauseAll() {
        this._audios.forEach((audio) => {
            audio.pause();
        });
    }
    resumeAll() {
        this._audios.forEach((audio) => {
            audio.resume();
        });
    }
    stopAll() {
        this._audios.forEach((audio) => {
            audio.stop();
        });
    }
}

export default new Audio();
